﻿/*
*      
*          ┌─┐       ┌─┐
*       ┌──┘ ┴───────┘ ┴──┐
*       │                 │
*       │       ───       │
*       │  ─┬┘       └┬─  │
*       │                 │
*       │       ─┴─       │
*       │                 │
*       └───┐         ┌───┘
*           │         │
*           │         │
*           │         │
*           │         └──────────────┐
*           │                        │
*           │                        ├─┐
*           │                        ┌─┘    
*           │                        │
*           └─┐  ┐  ┌───────┬──┐  ┌──┘         
*             │ ─┤ ─┤       │ ─┤ ─┤         
*             └──┴──┘       └──┴──┘ 
*                 神兽保佑 
*                 代码无BUG! 
*  Author shang
*  2022-07-22 18:48:24
*/

using Autofac.Annotation;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using TS.Model;

namespace TS.Services;

[Component(AutofacScope = AutofacScope.SingleInstance)]
public class QuartzService
{
    private IScheduler _scheduler;
    [Autowired] public IOCJobFactory _JobFactory { get; set; }

    SemaphoreSlim slim = new SemaphoreSlim(1); //允许最多1

    public async Task<IScheduler> GetScheduler(int maxconcurrencey = 50)
    {
        slim.Wait();
        if (_scheduler == null)
            _scheduler = await SchedulerBuilder.Create()
                .UseDefaultThreadPool(x => x.MaxConcurrency = maxconcurrencey)
                .BuildScheduler();

        slim.Release();
        return _scheduler;
    }

    #region 扩展方法

    public async Task<IScheduler> CreateQuartzTask(TaskOptions task)
    {
        IJobDetail job;
        //新建任务 
        if (task.RequestType == "post" || task.RequestType == "get")
        {
            job = JobBuilder.Create<HttpResultfulJob>()
                .WithIdentity(task.TaskName, task.GroupName)
                .Build();
        }
        else
        {
            job = JobBuilder.Create<ShellJob>()
                .WithIdentity(task.TaskName, task.GroupName)
                .Build();
        }


        ///触发器
        ITrigger trigger = TriggerBuilder.Create()
            .WithIdentity(task.TaskName, task.GroupName)
            .StartNow()
            .WithDescription(task.Describe)
            .WithCronSchedule(task.Interval)
            .Build();
        IScheduler scheduler = await GetScheduler();
        ///这个很重要 这个才能保证每个job的 注入是有效的 
        if (_JobFactory != null)
        {
            scheduler.JobFactory = _JobFactory;
        }

        await scheduler.ScheduleJob(job, trigger);

        return scheduler;
    }

    /// <summary>
    /// 暂停
    /// </summary>
    /// <param name="taskOptions"></param>
    /// <returns></returns>
    public async Task<HttpResult> Pause(TaskOptions taskOptions)
    {
        return await TriggerAction(taskOptions.TaskName, taskOptions.GroupName, Enums.JobAction.暂停, taskOptions);
    }

    public async Task<HttpResult> Update(TaskOptions taskOptions)
    {
        return await TriggerAction(taskOptions.TaskName, taskOptions.GroupName, Enums.JobAction.修改, taskOptions);
    }

    /// <summary>
    /// 启动作业
    /// </summary>
    /// <param name="schedulerFactory"></param>
    /// <param name="taskOptions"></param>
    /// <returns></returns>
    public async Task<HttpResult> Start(TaskOptions taskOptions)
    {
        return await TriggerAction(taskOptions.TaskName, taskOptions.GroupName, Enums.JobAction.开启, taskOptions);
    }

    /// <summary>
    /// 立即执行一次作业
    /// </summary>
    /// <param name="schedulerFactory"></param>
    /// <param name="taskOptions"></param>
    /// <returns></returns>
    public async Task<HttpResult> Run(TaskOptions taskOptions)
    {
        return await TriggerAction(taskOptions.TaskName, taskOptions.GroupName, Enums.JobAction.立即执行, taskOptions);
    }

    /// <summary>
    /// 移除作业
    /// </summary>
    /// <param name="schedulerFactory"></param>
    /// <param name="taskName"></param>
    /// <param name="groupName"></param>
    /// <returns></returns>
    public async Task<HttpResult> Remove(TaskOptions taskOptions)
    {
        return await TriggerAction(taskOptions.TaskName, taskOptions.GroupName, Enums.JobAction.删除, taskOptions);
    }

    public async Task<HttpResult> TriggerAction(string taskName, string groupName, Enums.JobAction action,
        TaskOptions taskOptions = null)
    {
        HttpResult res = new HttpResult();
        string errorMsg = "";
        try
        {
            List<JobKey> jobKeys = _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)).Result.ToList();
            if (jobKeys == null || jobKeys.Count() == 0)
            {
                errorMsg = $"未找到分组[{groupName}]";
                res.code = 1;
                res.msg = errorMsg;
                return res;
            }

            JobKey jobKey = jobKeys
                .Where(s => _scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == taskName))
                .FirstOrDefault();
            if (jobKey == null)
            {
                errorMsg = $"未找到触发器[{taskName}]";
                res.code = 1;
                res.msg = errorMsg;
                return res;
            }

            var triggers = await _scheduler.GetTriggersOfJob(jobKey);
            ITrigger trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == taskName).FirstOrDefault();

            if (trigger == null)
            {
                errorMsg = $"未找到触发器[{taskName}]";
                res.code = 1;
                res.msg = errorMsg;
                return res;
            }

            switch (action)
            {
                case Enums.JobAction.删除:
                case Enums.JobAction.修改:
                    await _scheduler.PauseTrigger(trigger.Key);
                    await _scheduler.UnscheduleJob(trigger.Key); // 移除触发器
                    await _scheduler.DeleteJob(trigger.JobKey);
                    await CreateQuartzTask(taskOptions);
                    //result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                    break;
                case Enums.JobAction.暂停:
                case Enums.JobAction.停止:
                case Enums.JobAction.开启:
                    //result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                    if (action == Enums.JobAction.暂停)
                    {
                        await _scheduler.PauseTrigger(trigger.Key);
                    }
                    else if (action == Enums.JobAction.开启)
                    {
                        await _scheduler.ResumeTrigger(trigger.Key);
                        await _scheduler.ResumeJob(jobKey);
                    }
                    else
                    {
                        await _scheduler.Shutdown();
                    }

                    break;
                case Enums.JobAction.立即执行:
                    await _scheduler.TriggerJob(jobKey);
                    break;
            }

            res.msg = $"作业{action.ToString()}成功";
        }
        catch (Exception ex)
        {
            errorMsg = ex.Message;

            res.code = 1;
            res.msg = errorMsg;
        }

        return res;
    }

    #endregion
}